package org.mikrosopht.movievote.client;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.gen2.table.client.FixedWidthFlexTable;
import com.google.gwt.gen2.table.client.FixedWidthGrid;
import com.google.gwt.gen2.table.client.PagingScrollTable;
import com.google.gwt.gen2.table.client.SelectionGrid.SelectionPolicy;
import com.google.gwt.gen2.table.client.AbstractScrollTable.ScrollPolicy;
import com.google.gwt.gen2.table.client.CachedTableModel;
import com.google.gwt.gen2.table.client.TableDefinition;
import com.google.gwt.gen2.table.client.DefaultTableDefinition;
import com.google.gwt.gen2.table.client.DefaultRowRenderer;
import com.google.gwt.gen2.table.client.RowRenderer;
import com.google.gwt.gen2.table.client.AbstractColumnDefinition;
import com.google.gwt.gen2.table.client.FixedWidthGridBulkRenderer;
import com.google.gwt.gen2.table.client.ScrollTable;
import com.google.gwt.gen2.table.client.TableModelHelper.ColumnSortInfo;
import com.google.gwt.gen2.table.client.TableModelHelper.ColumnSortList;
import com.google.gwt.gen2.table.event.client.PageLoadHandler;
import com.google.gwt.gen2.table.event.client.PageLoadEvent;
import com.google.gwt.gen2.table.event.client.PageChangeEvent;
import com.google.gwt.gen2.table.event.client.PageChangeHandler;


public class MovieList extends Composite implements PageLoadHandler, MovieListPagerEventReceiver, VoteWidgetEventReceiver, 
    MovieListSearcherEventReceiver, MovieListPrunerEventReceiver, TallyWidgetEventReceiver, PageChangeHandler {
  private static final int ROWS_PER_PAGE = 25;

  private PagingScrollTableInitialSorting<Movie> pagingScrollTable;
  private MovieListPager pager;
  private VerticalPanel movieListPanel = new VerticalPanel();
  private HorizontalPanel controllerPanel = new HorizontalPanel();
  private MovieDataSourceTableModel tableModel = new MovieDataSourceTableModel();
  private CachedTableModel<Movie> cachedTableModel;
  private FixedWidthGrid dataTable;
  private MovieListSearcher searcher;
  private MovieListPruner pruner;
  private int sortColumn = -1;

  public MovieList(MovieListSort sort1, MovieListSort sort2) {
    pager = new MovieListPager(this);
    searcher = new MovieListSearcher(this);
    pruner = new MovieListPruner(this);
    controllerPanel.add(pager);
    controllerPanel.add(searcher);
    controllerPanel.add(pruner);
    controllerPanel.setCellVerticalAlignment(pruner, HasVerticalAlignment.ALIGN_MIDDLE);
    tableModel.setSorting(sort1, sort2);
    if (sort1 == MovieListSort.SCREENING_DATE_DESCENDING) {
      pruner.setScreeningsChecked(true);
      pruner.setUnratedChecked(false);
      // sort1 is meaningless here, but set it to something sane after leaving screening view.
      sort1 = MovieListSort.TALLY_DESCENDING;
    }
    if (sort1 == MovieListSort.UNRATED) {
      pruner.setScreeningsChecked(false);
      pruner.setUnratedChecked(true);
      // sort1 is meaningless here, but set it to something sane after leaving screening view.
      sort1 = MovieListSort.TALLY_DESCENDING;
    }

    FixedWidthFlexTable headerTable = createHeaderTable(tableModel);
    dataTable = new FixedWidthGrid();
    dataTable.getColumnSortList().add(new ColumnSortInfo(getSortColumn(sort1), getSortDirection(sort1)));
    
    cachedTableModel = new CachedTableModel<Movie>(tableModel);
    cachedTableModel.setPreCachedRowCount(ROWS_PER_PAGE * 2);
    cachedTableModel.setPostCachedRowCount(ROWS_PER_PAGE * 2);

    TableDefinition<Movie> tableDefinition = createTableDefinition();
    pagingScrollTable = new PagingScrollTableInitialSorting<Movie>(cachedTableModel, dataTable, headerTable, tableDefinition);
    pagingScrollTable.addPageLoadHandler(this);
    pagingScrollTable.addPageChangeHandler(this);
    pagingScrollTable.setPageSize(ROWS_PER_PAGE);
    pagingScrollTable.setEmptyTableWidget(new HTML("There is no data to display"));

    FixedWidthGridBulkRenderer<Movie> bulkRenderer = new FixedWidthGridBulkRenderer<Movie>(
      pagingScrollTable.getDataTable(), pagingScrollTable);
    pagingScrollTable.setBulkRenderer(bulkRenderer);

    pagingScrollTable.setCellPadding(0);
    pagingScrollTable.setCellSpacing(0);
    pagingScrollTable.setResizePolicy(ScrollTable.ResizePolicy.FILL_WIDTH);
    pagingScrollTable.setScrollPolicy(ScrollPolicy.DISABLED);
    pagingScrollTable.setHeight("100%");
    pagingScrollTable.setSortIndicator(getSortColumn(sort1), getSortDirection(sort1));

    movieListPanel.add(controllerPanel);
    movieListPanel.add(pagingScrollTable);
    movieListPanel.setWidth("100%");
    initWidget(movieListPanel);
  }

  private int getSortColumn(MovieListSort sort) {
    if (sort == MovieListSort.NAME_ASCENDING) {
      return 0;
    } else if (sort == MovieListSort.NAME_DESCENDING) {
      return 0;
    } else if (sort == MovieListSort.TALLY_ASCENDING) {
      return 3;
    } else if (sort == MovieListSort.TALLY_DESCENDING) {
      return 3;
    } else if (sort == MovieListSort.DATE_ADDED_ASCENDING) {
      return 6;
    } else if (sort == MovieListSort.DATE_ADDED_DESCENDING) {
      return 6;
    } else if (sort == MovieListSort.REVIEW_SCORE_ASCENDING) {
      return 2;
    } else if (sort == MovieListSort.REVIEW_SCORE_DESCENDING) {
      return 2;
    }
    return 0;
  }

  private boolean getSortDirection(MovieListSort sort) {
    if (sort == MovieListSort.NAME_ASCENDING) {
      return true;
    } else if (sort == MovieListSort.NAME_DESCENDING) {
      return false;
    } else if (sort == MovieListSort.TALLY_ASCENDING) {
      return true;
    } else if (sort == MovieListSort.TALLY_DESCENDING) {
      return false;
    } else if (sort == MovieListSort.DATE_ADDED_ASCENDING) {
      return true;
    } else if (sort == MovieListSort.DATE_ADDED_DESCENDING) {
      return false;
    } else if (sort == MovieListSort.REVIEW_SCORE_ASCENDING) {
      return true;
    } else if (sort == MovieListSort.REVIEW_SCORE_DESCENDING) {
      return false;
    }
    return true;
  }

  private FixedWidthFlexTable createHeaderTable(MovieDataSourceTableModel model) {
    FixedWidthFlexTable headerTable = new FixedWidthFlexTable();
    headerTable.setHTML(0, 0, "Name");
    model.setNameColumnIndex(0);
    headerTable.setHTML(0, 1, "Reviews");
    headerTable.setHTML(0, 2, "Score");
    model.setReviewScoreColumnIndex(2);
    headerTable.setHTML(0, 3, "Tally");
    model.setTallyColumnIndex(3);
    headerTable.setHTML(0, 4, "Vote");
    headerTable.setHTML(0, 5, "Your Rating");
    headerTable.setHTML(0, 6, "Date Added");
    model.setDateAddedColumnIndex(6);
    return headerTable;
  }

  private class MovieRowRenderer implements RowRenderer<Movie> {
    private final String[] rowColors = new String[] {"#FFFFDD", "#EEEEEE"};
    private final String[] darkRowColors = new String[] {"#EEEEEE", "#C0C0C0"};
    private DefaultRowRenderer renderer;
    private DefaultRowRenderer darkRenderer;

    public MovieRowRenderer() {
      renderer = new DefaultRowRenderer<Movie>(rowColors);
      darkRenderer = new DefaultRowRenderer<Movie>(darkRowColors);
    }

    public void renderRowValue(Movie rowValue, TableDefinition.AbstractRowView<Movie> view) {
      if (tableModel.getShowScreeningsOnly()) {
        darkRenderer.renderRowValue(rowValue, view);
      } else if (rowValue.getScreenings() > 0) {
        view.setStyleAttribute("background", darkRowColors[1]);
      } else {
        renderer.renderRowValue(rowValue, view);
      }
    }
  }

  private TableDefinition<Movie> createTableDefinition() {
    DefaultTableDefinition<Movie> tableDefinition = new DefaultTableDefinition<Movie>();
    tableDefinition.setRowRenderer(new MovieRowRenderer());

    // Movie name
    {
      AbstractColumnDefinition<Movie, Widget> columnDef = new AbstractColumnDefinition<Movie, Widget>() {
        @Override
        public Widget getCellValue(Movie rowValue) {
          if (rowValue.getImdbUrl() != null) {
            HTML widget = new HTML("<a href=\"" + rowValue.getImdbUrl() + "\">" + rowValue.getName() + "</a>");
            return widget;
          } else {
            return(new Label(rowValue.getName()));
          }
        }
        @Override
        public void setCellValue(Movie rowValue, Widget cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(300);
      columnDef.setMaximumColumnWidth(300);
      columnDef.setPreferredColumnWidth(300);
      columnDef.setColumnSortable(true);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Reviews
    {
      AbstractColumnDefinition<Movie, Widget> columnDef = new AbstractColumnDefinition<Movie, Widget>() {
        @Override
        public Widget getCellValue(Movie rowValue) {
          Grid reviewGrid = new Grid(1, 3);
          HTML link;
          if (rowValue.getTrailerUrl() != null) {
            link = new HTML("<a href=\"" + rowValue.getTrailerUrl() + "\"><img src=\"" + MovieVoteConstants.TRAILER_IMG + "\"></a>");
            reviewGrid.setWidget(0, 0, link);
          }
          if (rowValue.getRottenUrl() != null) {
            link = new HTML("<a href=\"" + rowValue.getRottenUrl() + "\"><img src=\"" + MovieVoteConstants.ROTTEN_IMG + "\"></a>");
            reviewGrid.setWidget(0, 1, link);
          }
          if (rowValue.getMetacriticUrl() != null) {
            link = new HTML("<a href=\"" + rowValue.getMetacriticUrl() + "\"><img src=\"" + MovieVoteConstants.METACRITIC_IMG + "\"></a>");
            reviewGrid.setWidget(0, 2, link);
          }
          return reviewGrid;
        }
        @Override
        public void setCellValue(Movie rowValue, Widget cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(75);
      columnDef.setMaximumColumnWidth(75);
      columnDef.setPreferredColumnWidth(75);
      columnDef.setColumnSortable(false);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Review Score
    {
      AbstractColumnDefinition<Movie, String> columnDef = new AbstractColumnDefinition<Movie, String>() {
        @Override
        public String getCellValue(Movie rowValue) {
          if (rowValue.getReviewScore() > 0) {
            return ("" + rowValue.getReviewScore());
          }
          return "";
        }
        @Override
        public void setCellValue(Movie rowValue, String cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(50);
      columnDef.setMaximumColumnWidth(50);
      columnDef.setPreferredColumnWidth(50);
      columnDef.setColumnSortable(true);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Tally
    {
      AbstractColumnDefinition<Movie, Widget> columnDef = new AbstractColumnDefinition<Movie, Widget>() {
        @Override
        public Widget getCellValue(Movie rowValue) {
          TallyWidget tallyWidget = new TallyWidget(rowValue.getName(), MovieList.this);
          tallyWidget.update(rowValue.getTally(), rowValue.getPlusVotes(), rowValue.getMinusVotes(), rowValue.getScreenings());
          return tallyWidget;
        }
        @Override
        public void setCellValue(Movie rowValue, Widget cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(125);
      columnDef.setMaximumColumnWidth(125);
      columnDef.setPreferredColumnWidth(125);
      columnDef.setColumnSortable(true);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Vote
    {
      AbstractColumnDefinition<Movie, Widget> columnDef = new AbstractColumnDefinition<Movie, Widget>() {
        @Override
        public Widget getCellValue(Movie rowValue) {
          VoteWidget voteWidget = new VoteWidget(rowValue.getName(), rowValue.getVote() > 0, rowValue.getVote() < 0, MovieList.this);
          return voteWidget;
        }
        @Override
        public void setCellValue(Movie rowValue, Widget cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(50);
      columnDef.setMaximumColumnWidth(50);
      columnDef.setPreferredColumnWidth(50);
      columnDef.setColumnSortable(false);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Rating
    {
      AbstractColumnDefinition<Movie, Widget> columnDef = new AbstractColumnDefinition<Movie, Widget>() {
        @Override
        public Widget getCellValue(Movie rowValue) {
          return new MovieRating(rowValue.getName(), rowValue.getRating());
        }
        @Override
        public void setCellValue(Movie rowValue, Widget cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(100);
      columnDef.setMaximumColumnWidth(100);
      columnDef.setPreferredColumnWidth(100);
      columnDef.setColumnSortable(false);
      tableDefinition.addColumnDefinition(columnDef);
    }

    // Date added
    {
      AbstractColumnDefinition<Movie, String> columnDef = new AbstractColumnDefinition<Movie, String>() {
        @Override
        public String getCellValue(Movie rowValue) {
          return (rowValue.getDateAdded());
        }
        @Override
        public void setCellValue(Movie rowValue, String cellValue) {
        }
      };
      columnDef.setMinimumColumnWidth(150);
      columnDef.setMaximumColumnWidth(150);
      columnDef.setPreferredColumnWidth(150);
      columnDef.setColumnSortable(true);
      tableDefinition.addColumnDefinition(columnDef);
    }

    return tableDefinition;
  }

  private void updatePagerButtons() {
    int currentPage = pagingScrollTable.getCurrentPage();
    if (currentPage <= 0) {
      pager.setFirstDisabled();
      pager.setPreviousDisabled();
    } else {
      pager.setFirstEnabled();
      pager.setPreviousEnabled();
    }
    if (currentPage == (pagingScrollTable.getPageCount() - 1)) {
      pager.setLastDisabled();
      pager.setNextDisabled();
    } else {
      pager.setLastEnabled();
      pager.setNextEnabled();
    }
  }

  public void onPreviousClick() {
    pagingScrollTable.gotoPreviousPage();
  }

  public void onFirstClick() {
    pagingScrollTable.gotoFirstPage();
  }

  public void onLastClick() {
    pagingScrollTable.gotoLastPage();
  }

  public void onNextClick() {
    pagingScrollTable.gotoNextPage();
  }

  public void onVoteChange(Movie movie) {
    cachedTableModel.clearCache();
    pagingScrollTable.reloadPage();
  }

  public void onTextChange(String text) {
    tableModel.setSearch(text);
    listSizeChange();
  }

  public void onScreeningsClick(boolean checked) {
    if (tableModel.getShowScreeningsOnly() != checked) {
      tableModel.setShowScreeningsOnly(checked);
      if (checked) {
        tableModel.setShowUnratedOnly(!checked);
      }
      listSizeChange();
    }
  }

  public void onUnratedClick(boolean checked) {
    if (tableModel.getShowUnratedOnly() != checked) {
      tableModel.setShowUnratedOnly(checked);
      if (checked) {
        tableModel.setShowScreeningsOnly(!checked);
      }
      listSizeChange();
    }
  }

  public void onPageLoad(PageLoadEvent event) {
    updatePagerButtons();
  }

  public void onTreeSizeChange() {
    pagingScrollTable.redraw();
  }

  public void onPageChange(PageChangeEvent event) {
    /* Make the initial sorting on a new column click descending */
    ColumnSortList sortList = dataTable.getColumnSortList();
    if (sortList != null) {
      if (sortColumn != -1 && sortColumn != sortList.getPrimaryColumn()) {
        sortList.getPrimaryColumnSortInfo().setAscending(false);
      }
    }
    sortColumn = sortList.getPrimaryColumn();
  }

  private void listSizeChange() {
    cachedTableModel.clearCache();
    tableModel.setRowCount(-1);
    pagingScrollTable.gotoFirstPage();
    pagingScrollTable.reloadPage();
  }
}
